package io.kiki.sba.registry.server.metadata.resource;

import io.kiki.sba.registry.common.model.GenericResponse;
import io.kiki.sba.registry.common.model.console.PersistenceData;
import io.kiki.sba.registry.common.model.metaserver.ProvideDataChangeEvent;
import io.kiki.sba.registry.common.model.store.DataInfo;
import io.kiki.sba.registry.core.model.Result;
import io.kiki.sba.registry.server.metadata.provide.data.DefaultProvideDataNotifier;
import io.kiki.sba.registry.server.metadata.provide.data.ProvideDataService;
import io.kiki.sba.registry.server.metadata.resource.filter.LeaderAwareRestController;
import io.kiki.sba.registry.store.api.DBResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;


@Path("persistentData")
@LeaderAwareRestController
public class ProvideDataResource {

    private static final Logger logger = LoggerFactory.getLogger(ProvideDataResource.class);


    @Autowired
    private ProvideDataService provideDataService;

    @Autowired
    private DefaultProvideDataNotifier provideDataNotifier;

    @GET
    @Path("query")
    @Produces(MediaType.APPLICATION_JSON)
    public GenericResponse<PersistenceData> query(@QueryParam("dataInfoId") String dataInfoId) {
        DBResponse<PersistenceData> queryResponse = provideDataService.queryProvideData(dataInfoId);
        return new GenericResponse<PersistenceData>().fillSucceed(queryResponse.getEntity());
    }

    @POST
    @Path("put")
    @Produces(MediaType.APPLICATION_JSON)
    public Result put(PersistenceData data) {

        checkObj(data, "PersistenceData");
        checkString(data.getData());
        checkObj(data.getVersion(), "version");

        String dataInfoId = DataInfo.toDataInfoId(data.getInstanceId(), data.getDataId(), data.getGroup());

        boolean ret;
        try {
            ret = provideDataService.saveProvideData(data);
            logger.info("put Persistence Data {} to DB result {}!", data, ret);
        } catch (Throwable e) {
            logger.error("error put Persistence Data {} to DB!", data, e);
            throw new RuntimeException("Put Persistence Data " + data + " to DB error!", e);
        }

        if (ret) {
            fireDataChangeNotify(data.getVersion(), dataInfoId);
        }

        Result result = new Result();
        result.setSuccess(ret);
        return result;
    }

    @POST
    @Path("remove")
    @Produces(MediaType.APPLICATION_JSON)
    public Result remove(PersistenceData data) {

        checkObj(data, "PersistenceData");
        checkObj(data.getVersion(), "version");

        String dataInfoId = DataInfo.toDataInfoId(data.getInstanceId(), data.getDataId(), data.getGroup());

        boolean ret;
        try {
            ret = provideDataService.removeProvideData(dataInfoId);
            logger.info("remove Persistence Data {} from DB result {}!", data, ret);
        } catch (Exception e) {
            logger.error("error remove Persistence Data {} from DB!", data);
            throw new RuntimeException("Remove Persistence Data " + data + " from DB error!");
        }

        if (ret) {
            fireDataChangeNotify(data.getVersion(), dataInfoId);
        }

        Result result = new Result();
        result.setSuccess(ret);
        return result;
    }

    private void fireDataChangeNotify(Long version, String dataInfoId) {

        ProvideDataChangeEvent provideDataChangeEvent = new ProvideDataChangeEvent(dataInfoId, version);

        if (logger.isInfoEnabled()) {
            logger.info("send PERSISTENCE_DATA_CHANGE_NOTIFY_TASK notifyProvideDataChange: {}", provideDataChangeEvent);
        }
        provideDataNotifier.notifyProvideDataChange(provideDataChangeEvent);
    }

    private void checkString(String input) {
        if (input == null || input.isEmpty()) {
            throw new IllegalArgumentException("Error String data input:" + input);
        }
    }

    private void checkObj(Object input, String objName) {
        if (input == null) {
            throw new IllegalArgumentException("Error null Object " + objName + " data input!");
        }
    }


    protected ProvideDataResource setProvideDataService(ProvideDataService provideDataService) {
        this.provideDataService = provideDataService;
        return this;
    }


    protected ProvideDataResource setProvideDataNotifier(DefaultProvideDataNotifier provideDataNotifier) {
        this.provideDataNotifier = provideDataNotifier;
        return this;
    }
}
